#include "tcpclient.h"
#include "ui_tcpclient.h"
#include "friend.h"
#include "msgadd.h"

#include <qlogging.h>
#include <QtMsgHandler>
#include <QMessageLogContext>
#include <QtMessageHandler>




TcpClient::TcpClient(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::TcpClient)
{
    ui->setupUi(this);


    //构造

    //调整大小
    resize(500, 300);

    //加载IP地址和端口号
    loadConfig();

    //提前关联connected信号和槽函数, 用来提醒客户端已经连接到了服务器
    connect(&m_tcpSocket, &QTcpSocket::connected, this, &TcpClient::showConnect);
//    connect(&m_tcpSocket, SIGNAL(connected()), this, SLOT(showConnect()));

    //服务器有消息发来的时候
    connect(&m_tcpSocket, &QTcpSocket::readyRead, this, &TcpClient::recvMsg);



    //连接服务器
    m_tcpSocket.connectToHost(QHostAddress(m_strIP), m_usPort);

    //Friend界面
    //绑定按钮m_pShowOnlineUsrPB
    connect(&Friend::getInstance(), &Friend::signals_sendToServerPBOnline, this, &TcpClient::slot_sendToServerPB_m_pShowOnlineUsrPB);
    //绑定按钮m_pSearchUsrPB
    connect(&Friend::getInstance(), &Friend::signals_sendToServerPBSearch, this, &TcpClient::slot_sendToServerPB_m_pSearchUsrPB);

    //MsgAdd界面
    connect(&MsgAdd::getInstance(), &MsgAdd::signals_sendToServerPBAddFriend, this, &TcpClient::slot_sendToServerPB_m_pMsgAddUsrPB);

    //Online界面
    //绑定按钮pb_addfriend
    connect(&Online::getInstance(), &Online::signal_toAddFriend, this, &TcpClient::slot_sendToServerPB_pb_addfriendPB);

}

TcpClient::~TcpClient()
{
    delete ui;
}

//加载IP地址和端口号配置
void TcpClient::loadConfig()
{
    QFile file(":/client.config");//加载文件
    bool fileopen = file.open(QIODevice::OpenModeFlag::ReadOnly);//指定打开方式
    if(fileopen)
    {
        QByteArray byteData = file.readAll();//把文件内容全部读取出来, 获得QByteArray类型数据
        QString strData = byteData.toStdString().c_str();//转换为char*, 获取到配置文件中的字符串
        //qcout << strData;

        strData.replace("\r\n", " ");//把字符串中的 "\r\n" 替换为 " "
        //qcout << strData;

        QStringList strList = strData.split(" ");//把字符串按照空格切分, 存储在QStringList中

        //查看strList中的字符串
//        for(int i = 0; i<strList.size(); i++)
//        {
//            qcout << strList[i];
//        }
        m_strIP = strList.at(0);//IP地址
        m_usPort = strList.at(1).toUShort();//端口号
    }
    else
    {
        QMessageBox::critical(this, "open config", "open config failed");
    }

    file.close();

}

//单例
TcpClient &TcpClient::getInstance()
{
    static TcpClient instance;
    return instance;
}

//自定义槽函数, 信号处理函数, 当客户端连接服务器成功后, 这个槽函数来响应
void TcpClient::showConnect()
{
    qcout << "连接服务器成功, qt5";
}

//客户端接收服务器消息, 处理逻辑和服务器接收消息处理逻辑一样
void TcpClient::recvMsg()
{
    //接收数据, 当前可读的消息有多少
    qcout << m_tcpSocket.bytesAvailable();

    uint uiPDULen = 0;
    m_tcpSocket.read((char*)&uiPDULen, sizeof(uint));//先接收数据总长度, 最前面的四个字节, 后面数据在后面接收

    //实际消息的长度, 最后一个参数的大小
    uint uiMsgLen = uiPDULen - sizeof(PDU);

    //申请一个空间来存储接收的消息
    PDU* pdu = mkPDU(uiMsgLen);
    //再次接收后面的数据, 接收数据的时候先偏移出开始接收的四个字节的总数据的位置
    m_tcpSocket.read((char*)pdu+sizeof(uint), uiPDULen - sizeof(uint));

    //根据消息类型, 选择操作
    switch(pdu->uiMsgType)
    {
    case ENUM_MSG_TYPE_REGIST_RESPOND:
    {
        //用户注册成功回复消息
        handleRegist_tcpsocket(pdu);
        break;
    }

    case ENUM_MSG_TYPE_LOGIN_RESPOND:
    {
        //用户登录收到服务器回复的消息
        handleLogin_tcpsocket(pdu);
        break;
    }
    case ENUM_MSG_TYPE_ALL_ONLINE_RESPOND:
    {
        //用户查询所有在线用户服务器回复的消息
        handleAllOnline_tcpsocket(pdu);
        break;
    }
    case ENUM_MSG_TYPE_SEARCH_USR_RESPOND:
    {
        //用户查询搜锁的用户, 服务器回复的消息
        handleSearchUsr_tcpsocket(pdu);
        break;
    }
    case ENUM_MSG_TYPE_ADD_FRIEND_RESPOND:
    {
        //用户添加好友, 服务器回复的消息
        handleAddFriend_tcpsocket(pdu);
    }
    case ENUM_MSG_TYPE_ADD_FRIEND_REQUEST_WAIT:
    {
        //服务器发来消息, 问是否同意添加好友
        handleAddFriendRequest_tcpsocket(pdu);
    }
    case ENUM_MSG_TYPE_ADD_FRIEND_RESPOND_AGREE:
    {
        //服务器发来消息, 对方同意添加好友
        handleAddFriend_respond_agree_tcpsocket(pdu);
    }
    case ENUM_MSG_TYPE_ADD_FRIEND_RESPOND_REFUSE:
    {
        handleAddFriend_respond_refuse_tcpsocket(pdu);
    }



        default:
            break;

    }



    //测试输出
    qcout << "pdu->uiMsgType : " << pdu->uiMsgType;
    qcout << "pdu->caMsg : " << (char*)pdu->caMsg ;

    free(pdu);
    pdu=NULL;
}

//客户端处理注册请求回复
void TcpClient::handleRegist_tcpsocket(const PDU *pdu)
{
    if(0 == strcmp(pdu->caData, REGIST_OK))
    {
        qcout << "服务器返回了注册成功的消息";
    }
    else if(0 == strcmp(pdu->caData, REGIST_FAILED))
    {
        qcout << REGIST_FAILED;
    }
}

//客户端处理登录请求回复
void TcpClient::handleLogin_tcpsocket(const PDU *pdu)
{
    switch(pdu->caData[0])
    {
        case ENUM_LOGIN_ERROR_EMPTY_TYPE:
            ui->label_view->setText("用户名或密码为空");
            break;
        case ENUM_LOGIN_ERROR_NAME_TYPE:
            ui->label_view->setText("用户名错误");
            break;
        case ENUM_LOGIN_ERROR_PWD_TYPE:
            ui->label_view->setText("密码错误");
            break;
        case ENUM_LOGIN_ERROR_ONLINE_TYPE:
            ui->label_view->setText("用户已在线");
            break;
        case ENUM_LOGIN_ERROR_ALLOW_TYPE:
            ui->label_view->setText("允许登录");
            OpeWidget::getInstance().show();//显示主界面操作框
            Online::getInstance().setLabelUsr(m_loginName);
            this->hide();//登录界面隐藏
            break;
    }
}

//返回Friend界面类
//用户查询所有在线用户服务器回复的消息
void TcpClient::handleAllOnline_tcpsocket(const PDU* pdu)
{
    Friend::getInstance().showAllOnlineUsr(pdu);
}
//用户查询搜索的用户服务器回复的消息
void TcpClient::handleSearchUsr_tcpsocket(const PDU *pdu)
{
    Friend::getInstance().showSearchUsr(pdu);
}
//返回Online界面类
//用户添加好友服务器回复的消息
void TcpClient::handleAddFriend_tcpsocket(const PDU *pdu)
{
    Online::getInstance().showAddFriend(pdu);
}
//返回给OpeWdiget界面类
//通知
//服务器发来消息, 问是否同意添加好友
void TcpClient::handleAddFriendRequest_tcpsocket(const PDU *pdu)
{
    qcout << "服务器发来消息, 问是否同意添加好友";
    //处理
    OpeWidget::getInstance().haveMsgFromServer(pdu);
}
//返回给MsgAdd界面类
//服务器发来消息, 对方同意
void TcpClient::handleAddFriend_respond_agree_tcpsocket(const PDU *pdu)
{
    OpeWidget::getInstance().haveMsgFromServer_agree(pdu);
}
//服务器发来消息, 对方拒绝
void TcpClient::handleAddFriend_respond_refuse_tcpsocket(const PDU *pdu)
{
    qcout << "服务器发来消息, 拒绝好友";
    OpeWidget::getInstance().haveMsgFromServer_refuse(pdu);
}




//响应Friend类的槽函数
//按钮m_pShowOnlineUsrPB
//ENUM_FRIEND_PUSHBUTTON_m_pShowOnlineUsrPB
//参数:
//      enum ENUM_PUSHBUTTON_TYPE e, 按钮宏
void TcpClient::slot_sendToServerPB_m_pShowOnlineUsrPB()
{
    //向服务器发送请求信息, 请求所有在线用户的数据
    PDU *pdu = mkPDU(0);//不需要额外申请空间
    pdu->uiMsgType = ENUM_MSG_TYPE_ALL_ONLINE_REQUEST;//查询所有在线用户请求

    m_tcpSocket.write((char*)pdu, pdu->uiPDULen);

    free(pdu);//释放
    pdu=NULL;

    qcout << "向OpeWidget发送了信号";
    //TcpClient向OpeWidget发送信号,由按钮m_pShowOnlineUsrPB触发, 要求OpeWidget输出日志信息
    emit signal_slot_sendToServerPB_m_pShowOnlineUsrPB();

}
//响应Friend类的槽函数
//按钮m_pSearchUsrPB
void TcpClient::slot_sendToServerPB_m_pSearchUsrPB(QString usrName)
{
    if(usrName.isEmpty())
    {
        qcout << "要搜索的用户名是空";
        return;
    }
    //向服务器发送请求信息, 请求搜索用户的数据
    PDU *pdu = mkPDU(0);//不需要额外申请空间
    pdu->uiMsgType = ENUM_MSG_TYPE_SEARCH_USR_REQUEST;//查询所有在线用户请求
    qcout << "客户端搜索的用户: " << usrName;
    strncpy(pdu->caData, usrName.toStdString().c_str(), usrName.size());//用户名
    m_tcpSocket.write((char*)pdu, pdu->uiPDULen);

    free(pdu);//释放
    pdu=NULL;

    //TcpClient向OpeWidget发送信号,由按钮m_pSearchUsrPB触发, 要求OpeWidget输出日志信息
    emit signal_slot_sendToServerPB_m_pSearchUsrPB();
}

//响应MsgAdd界面类的槽函数
//把reply传给服务器, 告诉服务器添加好友的结果, 添加还是不添加
void TcpClient::slot_sendToServerPB_m_pMsgAddUsrPB(QString fName, int reply)
{


    qcout << "添加0 或者 不添加1, reply = "<< reply;
    PDU* resPdu = mkPDU(1);

    resPdu->uiMsgType = ENUM_ADDFRIEND_TYPE_NOTIFY;//回复服务器添加好友的请求

    strncpy(resPdu->caData, fName.toStdString().c_str(), 32);//从接收的数据中提取前32个字符给caName, 这个作为用户名
    strncpy(resPdu->caData+32, m_loginName.toStdString().c_str(), 32);//从接收的数据中提取后32个字符给caName, 这个作为密码

    resPdu->caMsg[0] = reply;

    qcout << "请求发出者 : " << fName;
    qcout << "被邀请求者 : " << m_loginName;
    qcout << "reply = " <<resPdu->caMsg[0];

    if( 0 == reply )
    {
        //同意
        qcout << "替换reply 0 : " << reply;
        resPdu->caMsg[0] = ENUM_ADDFRIEND_TYPE_AGREE;
    }
    else if(1 == reply)
    {
        //不同意
        qcout << "替换reply 1 : " << reply;
        resPdu->caMsg[0] = ENUM_ADDFRIEND_TYPE_REFUSE;
    }

    qcout << "reply = " <<resPdu->caMsg[0];
    m_tcpSocket.write((char*)resPdu, resPdu->uiPDULen);

    free(resPdu);//释放
    resPdu=NULL;


}


//响应Online类的槽函数
//按钮pb_addfriend
void TcpClient::slot_sendToServerPB_pb_addfriendPB(QString selectName)
{

    PDU* pdu = mkPDU(0);
    pdu->uiMsgType = ENUM_MSG_TYPE_ADD_FRIEND_REQUEST;//请求添加好友
    strncpy(pdu->caData, m_loginName.toStdString().c_str(), selectName.size());
    strncpy(pdu->caData+32, selectName.toStdString().c_str(), m_loginName.size());
    qcout << "m_loginName = " << m_loginName;
    qcout << "selectName = " << selectName;
    qcout << "selectName.size() = " << selectName.size();

    m_tcpSocket.write((char*)pdu, pdu->uiPDULen);

    free(pdu);
    pdu = NULL;

}

//获取自己的登录名
QString TcpClient::getmloginName()
{
    return m_loginName;
}




//发送按钮的槽函数
void TcpClient::on_send_pb_clicked()
{
    //获取lineEdit输入框的内容
    QString strMsg = ui->lineEdit->text();
    if(!strMsg.isEmpty())
    {
        //申请数据空间
        PDU* pdu = mkPDU(strMsg.size());
        pdu->uiMsgType = 8;//任意消息类型
        memcpy(pdu->caMsg, strMsg.toStdString().c_str(), strMsg.size());

        //发送数据
        m_tcpSocket.write((char*)pdu, pdu->uiPDULen);//发送数据

        //释放数据资源
        free(pdu);
        pdu = NULL;
    }
    else
    {
        qcout << "发送消息不能为空";
    }
}

//登录按钮
void TcpClient::on_pb_login_clicked()
{
    QString strName = ui->line_name->text();
    QString strPwd = ui->line_pwd->text();

    m_loginName = strName;//获取登录名

    if(!strName.isEmpty() && !strPwd.isEmpty())
    {
        PDU *pdu = mkPDU(0);//不需要后面申请的内存空间
        pdu->uiMsgType = ENUM_MSG_TYPE_LOGIN_REQUEST;//消息类型是登录请求消息
        strncpy(pdu->caData, strName.toStdString().c_str(), strName.size());//用户名
        strncpy(pdu->caData+32, strPwd.toStdString().c_str(), strName.size());//密码向后偏移32个字符
        m_tcpSocket.write((char*)pdu, pdu->uiPDULen);//发送
        free(pdu);//释放
        pdu=NULL;
    }
    else
    {
        qcout << "用户名或密码为空, 登录失败";
        ui->label_view->setText("用户名或密码为空, 登录失败");
    }
}

//注册按钮
void TcpClient::on_pb_regist_clicked()
{
    QString strName = ui->line_name->text();
    QString strPwd = ui->line_pwd->text();

    if(!strName.isEmpty() && !strPwd.isEmpty())
    {
        PDU *pdu = mkPDU(0);//不需要后面申请的内存空间
        pdu->uiMsgType = ENUM_MSG_TYPE_REGIST_REQUEST;//消息类型是注册请求消息
        strncpy(pdu->caData, strName.toStdString().c_str(), strName.size());//用户名
        strncpy(pdu->caData+32, strPwd.toStdString().c_str(), strName.size());//密码向后偏移32个字符
        m_tcpSocket.write((char*)pdu, pdu->uiPDULen);//发送
        free(pdu);//释放
        pdu=NULL;
    }
    else
    {
        qcout << "用户名或密码为空, 注册失败";
    }

}

//注销按钮
void TcpClient::on_pb_cancel_clicked()
{

}


